package org.jeecg.modules.simulator.model.line.station.station;

import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.simulator.Simulator;
import org.jeecg.modules.simulator.model.line.station.equipment.StationConcourser;
import org.jeecg.modules.simulator.model.line.station.equipment.StationEntryExitGate;
import org.jeecg.modules.simulator.model.line.station.equipment.StationPlatform;
import org.jeecg.modules.simulator.model.line.station.strategy.AverageEntryExitGateStage;
import org.jeecg.modules.simulator.model.line.station.strategy.GateLimitSpeedStage;
import org.jeecg.modules.simulator.model.line.station.strategy.IEntryExitGateStage;
import org.jeecg.modules.simulator.model.line.station.strategy.ILimitSpeedStage;
import org.jeecg.modules.simulator.model.person.Person;
import org.jeecg.modules.simulator.model.person.path.PersonPath;
import org.jeecg.modules.simulator.model.person.path.PersonState;
import lombok.Getter;
import lombok.Setter;
import org.jeecg.modules.simulator.recorder.StationInstantInfo;
import org.jeecg.modules.simulator.util.TimeUtil;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @program: gp2
 * @description:
 * @author: renzhg
 * @create: 2024-08-19 10:47
 **/
@Slf4j
public abstract class Station implements Simulator {
    private static final Logger logger = LoggerFactory.getLogger(Station.class);
    @Getter
    @Setter
    String line;
    @Getter
    @Setter
    String sceneCode;

    @Setter
    @Getter
    LocalTime DefaultStartTime;//需要给设置
    @Setter
    @Getter
    LocalTime DefaultEndTime;//需要给设置
    @Setter
    @Getter
    boolean isTransferStation = false;

//    @Setter
//    //历史每秒进站人数,KEY:时间
//    Map<String,Integer> historyInputNumPerSecMap=new HashMap<>();
//    @Setter
////od方向概率
//   Map<String,ODProbabilityApiInfo> historyODProbabilityApiInfoMap=new HashMap<>();


    @Getter
    @Setter
    String name = "未命名车站";
    @Getter
    @Setter
    String id;

    int limitSpeed;
    // 走行时长
    int walkTime;
    //车站的模拟的里程开始点,结束点
    @Getter
    @Setter
    long startPt, endPt;

    // 出入口
    @Getter
    @Setter
    public Map<String, StationEntryExitGate> entryExitGateMap = new HashMap<>(); // 进出站口数
    @Getter
    @Setter
    StationConcourser stationConcourser; //站厅
    //    Map<String, StationPlatform> stringStationPlatformMap = new HashMap<>();//站台
    @Getter
    @Setter
    StationPlatform stationPlatform;//站台
    @Getter
    @Setter
    boolean open;
    LocalTime currentTime;
    @Getter
    LocalTime operatingStartTime;
    @Getter
    LocalTime operatingCloseTime;

    @Getter
    @Setter
    StationInstantInfo stationInstantInfo =new StationInstantInfo();//站点人员记录打印使用

    public IEntryExitGateStage entryExitGateStage = new AverageEntryExitGateStage(); // 出入口分配策略
    public ILimitSpeedStage limitSpeedStage = new GateLimitSpeedStage(); //计算限流策略
    StationBizHandler bizHandler = new StationBizHandler(this);



    Integer maxPersonNum;


    public Integer getMaxPersonNum(){
        return stationConcourser.getMaxPersonNum()+stationPlatform.getMaxPersonNum();
    }
    public double getFullLoad() {
        int allNum=waitingPersonMap.size()+exchangePersonMap.size()+walkingPersonMap.size();
      return   (double)  allNum/getMaxPersonNum();

    }



    //region 人员集合
    @Getter
    @Setter
    Map<String, Person> allPersonMap = new HashMap<>();//所有人员
    @Getter
    @Setter
    Map<String, Person> outsidePersonMap = new HashMap<>();//外部想要进站人员
    @Getter
    Map<String, Person> walkingPersonMap = new HashMap<>();//进站走行中的人员
    @Getter
    @Setter
    Map<String, List<Person>> waitingPersonMap = new HashMap<>();
    @Getter
    @Setter
    Map<String, Person> exchangePersonMap = new HashMap<>();

    @Getter
    @Setter
    Map<String, Person> outStationPersonMap = new HashMap<>();//出站人员



    @Getter
    @Setter
    Map<String, PersonPath> personPathMap = new HashMap<>();


    @Getter
    @Setter
    Map<String, PersonPath> historyRoutePathMap = new HashMap<>();//key：routeid;

    @Getter
    @Setter
    PersonCreater personCreater;



    StationPublisher stationPublisher;

public int getWillInputStationNum(){

    int willInputStationNum = 0;
    for(Map.Entry<String,StationEntryExitGate> eegentry:entryExitGateMap.entrySet()){
        willInputStationNum=willInputStationNum+eegentry.getValue().getWillEntryPersonMap().size();

    }
    return  willInputStationNum;

}

    public void setOutStationPersons(Map<String, Person> mapPersopn) {
        this.outStationPersonMap.putAll(mapPersopn);

    }

    public void setWalkingPersonMap(Map<String, Person> mapPersopn) {
        this.walkingPersonMap.putAll(mapPersopn);

    }


    public void setOperatingCloseTime(String closeTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        if (TimeUtil.checkDateTime(closeTime).equals("DATE_AND_TIME")) {
            String timeStr = closeTime.substring(11);
            this.operatingCloseTime = LocalTime.parse(timeStr, formatter);
        } else if (TimeUtil.checkDateTime(closeTime).equals("ONLY_TIME")) {
            this.operatingCloseTime = LocalTime.parse(closeTime, formatter);
        }else if(TimeUtil.checkDateTime(closeTime).equals("ONLY_HOUR")){
            DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("HH:mm");
            this.operatingCloseTime = LocalTime.parse(closeTime, formatter1);
        }
    }


    public void setOperatingStartTime(String startTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        if (TimeUtil.checkDateTime(startTime).equals("DATE_AND_TIME")) {
            String timeStr = startTime.substring(11);
            this.operatingStartTime = LocalTime.parse(timeStr, formatter);
        } else if (TimeUtil.checkDateTime(startTime).equals("ONLY_TIME")) {

            this.operatingStartTime = LocalTime.parse(startTime, formatter);
        }else if(TimeUtil.checkDateTime(startTime).equals("ONLY_HOUR")){
            DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("HH:mm");
            this.operatingStartTime = LocalTime.parse(startTime, formatter1);
        }
    }


    //根据历史数据加载进站人数
    //
//    public void loadHistoryPersons(int size,
//                                   Map<String,List<ODProbabilityApiInfo>> odProbabilityApiInfosMap,
//                                   Map<String, PersonPath> route2PersonPathMap) {
//        //如果od人数大于进站人数时
//        if(odProbabilityApiInfosMap.keySet().size()>size){
//            int m=0;
//                for(Map.Entry<String,List<ODProbabilityApiInfo>>  entry:odProbabilityApiInfosMap.entrySet()){
//                    Collections.sort(entry.getValue());
//                    if(m>size){
//                        return;
//                    }
//
//                    PersonPath personPath = route2PersonPathMap.get(entry.getValue().get(0).getRouteId());
//                    Person person = new Person(personPath);
//                    person.setId(UUID.randomUUID().toString());
//                    person.setInStationState();
//                    this.outsidePersonMap.put(person.getId(), person);
//                    m++;
//                }
//        }
//
//        int infosize=(int) size/odProbabilityApiInfosMap.size();
//            for(Map.Entry<String,List<ODProbabilityApiInfo>>  entry:odProbabilityApiInfosMap.entrySet()){
//                for (int i = 0; i < entry.getValue().size(); i++) {
//                    String routeId = entry.getValue().get(i).getRouteId();
//                    double num = Double.parseDouble(entry.getValue().get(i).getRouteRate()) * 0.01 * infosize;
//                    int result = (int) Math.floor(num);
//                    PersonPath personPath = route2PersonPathMap.get(routeId);
//                    for (int j = 0; j < result; j++) {
//                        Person person = new Person(personPath);
//                        person.setId(UUID.randomUUID().toString());
//                        person.setInStationState();
//                        this.outsidePersonMap.put(person.getId(), person);
//                    }
//                }
//
//                PersonPath personPath = route2PersonPathMap.get(entry.getValue().get(0).getRouteId());
//                Person person = new Person(personPath);
//                person.setId(UUID.randomUUID().toString());
//                person.setInStationState();
//                this.outsidePersonMap.put(person.getId(), person);
//            }
//
//    }
//     }



//            String routeId = odProbabilityApiInfos.get(i).getRouteId();
//            double num = Double.parseDouble(odProbabilityApiInfos.get(i).getRouteRate()) * 0.01 * size;
//            int result = (int) Math.floor(num);
//
//            PersonPath personPath = route2PersonPathMap.get(routeId);
//
//            for (int j = 0; j < result; j++) {
//                Person person = new Person(personPath);
//                person.setId(UUID.randomUUID().toString());
//                person.setInStationState();
//                this.outsidePersonMap.put(person.getId(), person);
//            }


//        }





//    public void loadPersons(int size) {
//        int pathSize = personPathMap.values().size();
////        personPathMap.forEach((k, v) -> {
//        List<PersonPath> alettList = personPathMap.values().stream()
//                .collect(Collectors.toList());
//
////            int personnum = (int) ((1/pathSize) * size );
//        for (int i = 0; i < size; i++) {
//            if (i % (pathSize - 1) == 0 && i != 0) {
//                PersonPath personPath = alettList.get(0);
//                Person person = new Person(personPath);
//                person.setId(UUID.randomUUID().toString());
//                person.setInStationState();
//                this.outsidePersonMap.put(person.getId(), person);
//            } else {
//                PersonPath personPath = alettList.get(i % (pathSize - 1));
//                Person person = new Person(personPath);
//                person.setId(UUID.randomUUID().toString());
//                person.setInStationState();
//                this.outsidePersonMap.put(person.getId(), person);
//
//            }
////                this.allPersonMap.put(person.getId(), person);
//
//        }
////        });
//
//
//    }
/**
 * @description: 
 *
 * @author:  renzhg 
 * @date:  2024/11/4 9:47
 **/
    public void init() {
        if(this.getId().equals("1043")){
            System.out.println("1043");
        }
        this.setWalkTime();
        stationInstantInfo.setStationName(this.name);
        stationInstantInfo.setStationId(this.id);
        stationInstantInfo.setSceneCode(this.sceneCode);
        
    }

    //本站的走行时间
    public void setWalkTime() {
        this.walkTime = stationConcourser.getCrossTime() + stationPlatform.getCrossTime();
    }

    public Integer getOutWalk2StationSide(){
        return  stationPlatform.getCrossTime()+this.stationConcourser.getCrossTime();
    }
//
//endregion


    public Station(String name) {
        this.name = name;
    }
//region 业务方法


    @Override
    public LocalTime getCurrentTime() {
        return this.currentTime;
    }

    @Override
    public void UpdateCurrentTime(LocalTime currentTime) {
        this.currentTime = currentTime;
    }

    @Override
    public void nextTime(String now, Integer step) {

       LocalTime nowTime=LocalTime.parse(now);
        stationInstantInfo.instantReset();
        stationInstantInfo.setTimePt(now);
        if (this.isOpen()) {
//            logger.debug(this.name);
            // 处理人流
            if(!this.outsidePersonMap.isEmpty()){
//                stationInstantInfo.setStationTotalAndInstantInputPersonNum( this.outsidePersonMap.size());
//                if(this.getId().equals("1043")){
//                    System.out.println("1043");
//                }

                this.handlerInsidePerson(now);//进站
            }
            if(!this.outStationPersonMap.isEmpty()){
                this.handlerOutsidePerson(now);//出站
            }
            int waitCount = (int)this.waitingPersonMap.values().stream()
                    .flatMap(List::stream) // 将 List<ODProbabilityApiInfo> 展平为流
                    .count();
            if(!this.outsidePersonMap.isEmpty()){
                System.out.println(this.outsidePersonMap.size());
            }


            if(!this.walkingPersonMap.isEmpty()){
                System.out.println(this.walkingPersonMap.size());
            }
            SetStionInstantInfo(now);
            //外面将要进入车站的人




//            stationInstantInfo.setStationTotalAndInstantInStationPersonNum(this.walkingPersonMap.size() + waitCount);
//            stationInstantInfo.setStationTotalAndInstantWalkPersonNum( this.walkingPersonMap.size());
//            stationInstantInfo.reCordNums(walkingPersonMap.size(), this.outStationPersonMap.size() , this.outsidePersonMap.size(),now);

//            stationInstantInfo.setStationTotalAndInstantWaitPersonNum( waitCount);
        }

    }


    private void SetStionInstantInfo(String now) {
        System.out.println(stationInstantInfo);

    }

    private void recordOutsidePersons(String now) {
        //出站的人
        int outPutInstantNum = this.outStationPersonMap.size() - stationInstantInfo.getStationTotalOutPersonNum();
        stationInstantInfo.setStationInstantOutPersonNum(outPutInstantNum);
        stationInstantInfo.insertStationInstantOutPersonNumMap(now, outPutInstantNum);
        stationInstantInfo.setStationTotalOutPersonNum(this.outStationPersonMap.size());
        stationInstantInfo.insertStationTotalOutPersonNumMap(now, this.outStationPersonMap.size());

    }

    private void recordWalkPersons(String now) {
        //进站走行的人
        int inputInstantNum=this.walkingPersonMap.size();
        stationInstantInfo.setStationInstantWalkPersonNum(inputInstantNum);
        stationInstantInfo.setStationInstantInStationPersonNum(inputInstantNum+this.waitingPersonMap.size());
        stationInstantInfo.insertStationInstantInStationPersonNumMap(now, inputInstantNum);
        stationInstantInfo.setStationTotalInputPersonNum(this.walkingPersonMap.size());
        stationInstantInfo.insertStationTotalInputPersonNumMap(now, this.walkingPersonMap.size());

    }

    private void recordOutSidePersons(String now) {
        int outsideInstantNum=this.outsidePersonMap.size();
        stationInstantInfo.setWillInStationInstantNum(outsideInstantNum);
        stationInstantInfo.insertWillInStationInstantNumMap(now, outsideInstantNum);
        stationInstantInfo.setStationTotalWillInStationTotalNum(outsideInstantNum);
        stationInstantInfo.insertWillInStationTotalNumMap(now);
        stationInstantInfo.setStationInstantInputPersonNum(outsideInstantNum);
    }


    /**
     * @description: 人流出站
     * @author: renzhg
     * @date: 2024/8/20 15:07
     **/
    protected void handlerOutsidePerson(String now) {
        Map<String, Person> tempPersonMap = new HashMap<>();
        tempPersonMap.clear();
        recordOutsidePersons(now);
        outStationPersonMap.forEach((k, v) -> {
            v.countDownOutputWalkTime();
            if (v.getPersonState() == PersonState.offStation) {
                tempPersonMap.put(k, v);
            }
        });
        tempPersonMap.forEach((k, v) -> {
            outStationPersonMap.remove(k);
        });

    }


    //进站
    protected void handlerInsidePerson(String now) {
        LocalTime localTime = LocalTime.parse(now);
        this.outside2Inside(this.getCurrentTime(), localTime);//进站->走行
        this.inside2Transfer(this.getCurrentTime(), localTime);//换乘动作保留
        this.inside2Platform(this.getCurrentTime(), localTime);//走行->站台
        walkingPersonMap.forEach((k, v) -> {
//            if(this.getId().equals("1043")){
//                System.out.println("1043");
//            }
            v.countDownInputWalkTime();
        });
        recordWalkPersons(now);//记录走行人

    }


    /**
     * @description: 换乘站站的走行规定，考虑到换乘站走行的情况会出现站内走行对线
     * 策略--比如1,10两条线，进站的时候从1线的进站口进站，经过1站展厅，进入10站的站台--这种情况给留个换乘站接口，
     * @author: renzhg
     * @date: 2024/8/20 15:50
     **/
    protected void inside2Transfer(LocalTime currentTime, LocalTime now) {

    }


    /**
     * @description: 将在站外的人放入到进站走行的人员集合中, 并更人员状态
     * @author: renzhg
     * @date: 2024/8/20 15:49
     **/
    protected void outside2Inside(LocalTime currentTime, LocalTime now) {

        // 10000人，10人
        try {
            //这一时刻没人进站
            if(this.outsidePersonMap.isEmpty()){
                return;
            }
            receivePerson(this.outsidePersonMap);
            recordOutSidePersons(TimeUtil.Lcaltime2String(now));//记录将要进站的人，因为下面很快将要移除this.outsidePersonMap的人
            List<Person> people = this.limitSpeedStage.computedLimitPerson(this.entryExitGateMap, currentTime, now);
            for (Person person : people) {
                person.setCountInputWalkTime(this.walkTime);
                person.setInStationState();
                this.outsidePersonMap.remove(person.getId());
                this.walkingPersonMap.put(person.getId(), person);
                if(this.getId().equals("0457")){
                    logger.info("进站人员："+person.getId());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * @description: 在站内的走行人群的人员集合中，
     * 1、检查每个人是否到达了站台（时间倒计时是否到店），
     * 2、到达站台的人员，从走行人员集合中移除，放入到站台等待人员集合中
     * @author: renzhg
     * @date: 2024/8/20 15:55
     **/
    protected void inside2Platform(LocalTime currentTime, LocalTime now) {

        if(this.getId().equals("1043")){
            System.out.println(now);
            System.out.println("1043");
        }

        List<Person> tempPeople = new ArrayList<>();
        // 检查每个人的时间是否超过走行时间，超过了就到达了站台
        this.walkingPersonMap.values().forEach(p -> {
            //
            if (p.getPersonState() == PersonState.waitTrain) { // 如果到达了
                tempPeople.add(p);
            }
        });

        for (Person person : tempPeople) {
            this.walkingPersonMap.remove(person.getId());
            String nextNodeName = person.getNextNodeName(this.name);
            String key = "";
            if (Objects.equals(nextNodeName, null)) {
                key = person.getPersonPath().getFirst().getStationName() + "-" + person.getPersonPath().getLast().getStationName();
            } else {
                key = nextNodeName;
            }
            List<Person> havePersons = waitingPersonMap.get(key);
            if (havePersons == null) {
                havePersons = new ArrayList<>();
            }
            havePersons.add(person);
            this.waitingPersonMap.put(key, havePersons);
        }

    }

    /**
     * @description: 这个方法现在有问题，需要具体讨论
     * @author: renzhg
     * @date: 2024/8/20 16:03
     **/
    void receivePerson(Map<String, Person> personMap) throws Exception {
        // 检查od
        if (false) {
            throw new Exception();
        }
        // 接收业务给的总人数

        // 根据出入口策略，分配人数
        this.entryExitGateStage.assignPerson(personMap, entryExitGateMap);
        this.allPersonMap.putAll(personMap);


    }
    //endregion
}
